#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/event.h"
#include "wx/window.h"
#include "wx/scrolbar.h"
#include "wx/radiobut.h"
#include "wx/containr.h"
#endif //WX_PRECOMP

#define TRACE_FOCUS _T("focus")

wxControlContainer::wxControlContainer( wxWindow *winParent ) {
  m_winParent = winParent;
  m_winLastFocused = NULL;
  m_inSetFocus = false;
}

bool wxControlContainer::AcceptsFocus() const {
  if( m_winParent->IsShown() && m_winParent->IsEnabled() ) {
    wxWindowList::compatibility_iterator node = m_winParent->GetChildren().GetFirst();
    if( !node ) {
      return true;
    }
    while( node ) {
      wxWindow *child = node->GetData();
      node = node->GetNext();
      if( child->AcceptsFocus() ) {
        return true;
      }
    }
  }
  return false;
}

void wxControlContainer::SetLastFocus( wxWindow *win ) {
  if( win != m_winParent ) {
    if( win ) {
      wxWindow *winParent = win;
      while( winParent != m_winParent ) {
        win = winParent;
        winParent = win->GetParent();
        wxASSERT_MSG( winParent, _T( "为不是我们孩子的窗口设置最后一个焦点?" ) );
      }
    }
    m_winLastFocused = win;
    if( win ) {
      wxLogTrace( TRACE_FOCUS, _T( "将最后一个焦点设置为 %s(%s)" ), win->GetClassInfo()->GetClassName(), win->GetLabel().c_str() );
    } else {
      wxLogTrace( TRACE_FOCUS, _T( "不再聚焦" ) );
    }
  }
  wxWindow *parent = m_winParent->GetParent();
  if( parent ) {
    wxChildFocusEvent eventFocus( m_winParent );
    parent->GetEventHandler()->ProcessEvent( eventFocus );
  }
}

void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event ) {
  wxWindow *parent = m_winParent->GetParent();
  bool goingDown = event.GetEventObject() == parent;
  const wxWindowList& children = m_winParent->GetChildren();
  if( event.IsWindowChange() && !goingDown ) {
    wxWindow *bookctrl = NULL;
    for( wxWindowList::const_iterator i = children.begin(),
         end = children.end();
         i != end;
         ++i ) {
      wxWindow * const window = *i;
      if( window->HasMultiplePages() ) {
        if( bookctrl ) {
          bookctrl = NULL;
          break;
        }
        bookctrl = window;
      }
    }
    if( bookctrl ) {
      wxNavigationKeyEvent eventCopy( event );
      eventCopy.SetEventObject( m_winParent );
      if( bookctrl->GetEventHandler()->ProcessEvent( eventCopy ) ) {
        return;
      }
    }
  }
  if( !children.GetCount() || event.IsWindowChange() ) {
    if( goingDown ||
        !parent || !parent->GetEventHandler()->ProcessEvent( event ) ) {
      event.Skip();
    }
    return;
  }
  const bool forward = event.GetDirection();
  wxWindowList::compatibility_iterator node, start_node;
  if( goingDown ) {
    m_winLastFocused = ( wxWindow * )NULL;
    node = forward ? children.GetFirst() : children.GetLast();
  } else {
    wxWindow *winFocus = event.GetCurrentFocus();
    if( !winFocus ) {
      winFocus = m_winLastFocused;
    }
    if( !winFocus ) {
      winFocus = wxWindow::FindFocus();
    }
    if( winFocus ) {
      start_node = children.Find( winFocus );
    }
    if( !start_node && m_winLastFocused ) {
      start_node = children.Find( m_winLastFocused );
    }
    if( !start_node ) {
      start_node = children.GetFirst();
    }
    node = forward ? start_node->GetNext() : start_node->GetPrevious();
  }
  for( ;; ) {
    if( start_node && node && node == start_node ) {
      break;
    }
    if( !node ) {
      if( !start_node ) {
        break;
      }
      if( !goingDown ) {
        wxWindow *focussed_child_of_parent = m_winParent;
        while( parent ) {
          if( focussed_child_of_parent->IsTopLevel() ) {
            break;
          }
          event.SetCurrentFocus( focussed_child_of_parent );
          if( parent->GetEventHandler()->ProcessEvent( event ) ) {
            return;
          }
          focussed_child_of_parent = parent;
          parent = parent->GetParent();
        }
      }
      node = forward ? children.GetFirst() : children.GetLast();
      continue;
    }
    wxWindow *child = node->GetData();
    if( child->AcceptsFocusFromKeyboard() ) {
      event.SetEventObject( m_winParent );
      wxPropagationDisabler disableProp( event );
      if( !child->GetEventHandler()->ProcessEvent( event ) ) {
        m_winLastFocused = child;
        child->SetFocusFromKbd();
      }
      event.Skip( false );
      return;
    }
    node = forward ? node->GetNext() : node->GetPrevious();
  }
  event.Skip();
}

void wxControlContainer::HandleOnWindowDestroy( wxWindowBase *child ) {
  if( child == m_winLastFocused ) {
    m_winLastFocused = NULL;
  }
}

bool wxControlContainer::DoSetFocus() {
  wxLogTrace( TRACE_FOCUS, _T( "聚焦于 wxPanel 0x%p." ), m_winParent->GetHandle() );
  if( m_inSetFocus ) {
    return true;
  }
  wxWindow *win = wxWindow::FindFocus();
  while( win ) {
    if( win == m_winParent ) {
      return true;
    }
    if( win->IsTopLevel() ) {
      break;
    }
    win = win->GetParent();
  }
  m_inSetFocus = true;
  bool ret = SetFocusToChild();
  m_inSetFocus = false;
  return ret;
}

void wxControlContainer::HandleOnFocus( wxFocusEvent& event ) {
  wxLogTrace( TRACE_FOCUS, _T( "OnFocus on wxPanel 0x%p, name: %s" ), m_winParent->GetHandle(), m_winParent->GetName().c_str() );
  DoSetFocus();
  event.Skip();
}

bool wxControlContainer::SetFocusToChild() {
  return wxSetFocusToChild( m_winParent, &m_winLastFocused );
}

bool wxSetFocusToChild( wxWindow *win, wxWindow **childLastFocused ) {
  wxCHECK_MSG( win, false, _T( "wxSetFocusToChild(): invalid window" ) );
  wxCHECK_MSG( childLastFocused, false, _T( "wxSetFocusToChild(): NULL child poonter" ) );
  if( *childLastFocused ) {
    if( ( *childLastFocused )->GetParent() == win ) {
      wxLogTrace( TRACE_FOCUS, _T( "SetFocusToChild() => last child (0x%p)." ), ( *childLastFocused )->GetHandle() );
      ( *childLastFocused )->SetFocus();
      return true;
    } else {
      *childLastFocused = ( wxWindow * )NULL;
    }
  }
  wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
  while( node ) {
    wxWindow *child = node->GetData();
    node = node->GetNext();
    if( child->AcceptsFocusFromKeyboard() && !child->IsTopLevel() ) {
      wxLogTrace( TRACE_FOCUS, _T( "SetFocusToChild() => first child (0x%p)." ), child->GetHandle() );
      *childLastFocused = child;
      child->SetFocusFromKbd();
      return true;
    }
  }
  return false;
}
